8.基于terraform二次开发 IaC 平台
为什么要使用 Terraform ?
在 Terraform 未问世之前,我们依赖人工手动操作来创建云厂商的资源。这种方式操作简单,但在实际应用中会出现多种问题:
- 资源管理缺乏统一性:没有一个集中的仓库记录资源的归属和规格变更等信息,导致管理混乱。
- 手工变更容易导致错误:手动操作可能会影响线上服务的正常运行,而且回滚操作困难。频繁的人机交互增加了出错的可能性,最糟糕的是可能会"点错"并且忘记原始状态。
- 重复资源的创建需要重复的人工页面操作,这不仅耗时,而且无法标准化。
使用 Terraform 带来的变化
Terraform 最直观的功能就是 Infrastructure as Code,将云上基础设施以代码的形式描述出来,那么每一个基础设施的详细参数都是清楚、明确的,可以很快的通过复制代码修改个别参数的方法创建出同类资源,标准且提效。
同时,Terraform 进行与云厂商交互操作的时候是非常严谨,在你更改完代码后,需要先用 terraform plan 进行变更预览,你可以看到你的更改会影响到线上的部分,确定后进行 terraform apply,一次变更尘埃落定。
使用 Terraform 后需要注意的是以下几点:首先是代码仓库(code repo),用于存储基础设施的代码。其次是用于存储线上基础设施状态的后端(backend),这是非常重要的一点。最关键的是在执行实际部署之前进行计划(plan),并进行审查(review)。Terraform 使用的是 HashiCorp 自定义的 HCL 语言,它并不难学,但入门需要一些努力。然而,要求所有的开发人员都掌握这门语言并不必要,也不切实际。
在同一个代码仓库中,如果面向所有人,很可能导致混乱的目录结构和过于具体的定义。因此,基础设施的维护应该由基础设施工程师来负责。然而,并不需要专门的人员来编写 Terraform,因此我们将其设计为一个流程化的工具。
流利说基于 terraform 二次开发云平台 Luban
流利说给研发提供的各种窗口链接都放在了一个叫做 Luban 的平台上,申请人只需要在前端选择必要的参数,提交申请,对于申请人来说需要做的事情就结束了,接下来就是等待审批结果。例如,我想要申请一个阿里云 ECS 实例,如果直接写 Terraform,那大概要写一个如下的文件:
resource "alicloud_instance" "instance" {
# cn-beijing
availability_zone = "cn-beijing-b"
security_groups = alicloud_security_group.group.*.id
# series III
instance_type = "ecs.n4.large"
system_disk_category = "cloud_efficiency"
system_disk_name = "test_foo_system_disk_name"
system_disk_description = "test_foo_system_disk_description"
image_id = "ubuntu_18_04_64_20G_alibase_20190624.vhd"
instance_name = "test_foo"
vswitch_id = alicloud_vswitch.vswitch.id
internet_max_bandwidth_out = 10
data_disks {
name = "disk2"
size = 20
category = "cloud_efficiency"
description = "disk2"
encrypted = true
kms_key_id = alicloud_kms_key.key.id
}
}
看起来好像很容易看懂但有的地方又有点疑惑,接着就要去查 alicloud provider documentation 各个参数的意思然后改参数,对于一个没有写过 Terraform 的人来说还是比较麻烦的。于是流利说做了一个前端申请页面后,只需要选择必定的参数,Luban 后端就会按既定的规则在相应目录下进行代码生成并触发 GitOps 流程,对于申请人来说,只需要等审批结果了。妈妈再也不用担心我不懂 hcl 语法了!!
部门领导在前端能看到申请记录以及 terraform plan
的日志详情,来检查是否 plan 结果是否符合预期,符合点击 approve 进入下个流程,由基础设施成员进行复审。
基础设施人员会在值班群中收到通知:
值班人员在 GitLab 上审批 approve 后触发mobius webhook 进行 terraform apply 即对线上做出变更,apply 成功后,mobius 自动进行代码 merge, 至此一个申请流程结束,而申请人也会在内部Chat上收到 Luban Bot发出的资源申请成功的通知。
如此,资源的申请就非常的严谨,也不需要人员专门去学习写 Terraform, 重点就放在 review terraform plan 输出的变化是不是符合预期,并且通过ChatBot的触发性通知,提升了效率。
流利说团队通过采用标准化的工作流程,以及利用 Terraform 提供的出色的变更审查机制,结合现有的前端工作流和 GitLab 的代码审查功能,成功避免了基础资源变更可能导致的异常情况,并且可追溯变更的历史。这一解决方案解决了线上基础资源缺乏标准化和无法复制行为的痛点。同时,结合自动化转换器和串联工具的开发,将标准化的基础设施直接输出,实现了未来多云管理的标准化、可复制和可追溯的工程化实践。这为一键启动云基础设施奠定了坚实的基础。
技术要点
需要对接 gitlab
- 监听 merge request、push 等事件
- 配置 webhook ,处理相应事件
- 自动化创建项目
- 配置人员对于仓库权限
state 需要存储到远端
Terraform 支持云基础架构的定义、预览和部署。使用 Terraform,您可以使用 HCL 语法创建配置文件。HCL 语法允许你指定云提供程序(例如 Azure)以及构成云基础结构的元素。创建配置文件后,您可以创建一个执行计划,允许您在部署基础结构更改之前预览这些更改。验证更改后,应用执行计划以部署基础结构。
默认情况下,Terraform 状态存储在本地,这并不理想,原因如下:
- 本地状态在团队或协作环境中效果不佳
- 状态包含敏感信息
- 在本地存储状态会增加意外删除的可能性
我们接下来讲讲解如何将 state 文件存储在远端:
开通 azure 账号
开通远程存储, apply 以下文件
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~>3.0"
}
}
}
provider "azurerm" {
features {}
}
resource "random_string" "resource_code" {
length = 5
special = false
upper = false
}
resource "azurerm_resource_group" "tfstate" {
name = "tfstate"
location = "East US"
}
resource "azurerm_storage_account" "tfstate" {
name = "tfstate${random_string.resource_code.result}"
resource_group_name = azurerm_resource_group.tfstate.name
location = azurerm_resource_group.tfstate.location
account_tier = "Standard"
account_replication_type = "LRS"
allow_blob_public_access = false
tags = {
environment = "staging"
}
}
resource "azurerm_storage_container" "tfstate" {
name = "tfstate"
storage_account_name = azurerm_storage_account.tfstate.name
container_access_type = "private"
}
- 使用 backend 配置 terraform
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~>3.0"
}
}
backend "azurerm" {
resource_group_name = "tfstate"
storage_account_name = "<storage_account_name>"
container_name = "tfstate"
key = "terraform.tfstate"
}
}
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "state-demo-secure" {
name = "state-demo"
location = "eastus"
}
hcl模版渲染
- 用户在页面上是点点点的操作,前端传给后端的只是一些参数,后端需要将其和预先配置的模版结合,生成一份可以正确运行的 tf 配置。
- 同一份参数,可能会跨云厂商,如何实现前端无感知